<?php
App::import('Config', 'SagePay.sage_pay_settings');
class PaymentController extends AppController {

	var $name = 'Payment';
	var $uses = array('User','Customer');
	var $permissions = array(
		'confirm' => array('admin','manag','users','copyr','firms','emplo')
	);
	var $SagePaySettings;
	
	public function beforeFilter(){
		parent::beforeFilter();
		
		$this->SagePaySettings =& new SagePaySettings();
//   		$this->options = array_merge($FileUploadSettings->defaults, $this->options);

		App::import('Helper', 'Store.Store');
		$this->Store = new StoreHelper();
	}
	
	function confirm(){
		//** Gather customer details from the session **
		$buer = $this->User->read(null,$this->Session->read('Auth.User.id'));

		$strCustomerEMail      = $buer['User']["email"];
		$strBillingFirstnames  = $buer['Customer'][0]["name"];
		$strBillingSurname     = $buer['Customer'][0]["surname"];
		$strBillingAddress1    = $buer['Customer'][0]["address1"];
		$strBillingAddress2    = $buer['Customer'][0]["address2"];
		$strBillingCity        = $buer['Customer'][0]["address3"];
		$strBillingPostCode    = $buer['Customer'][0]["address4"];
		$strBillingCountry     = $buer['Customer'][0]["address5"];
		$strBillingState       = ''; //$buer['Customer'][0]["strBillingState"];
		$strBillingPhone       = $buer['Customer'][0]["phone1"];
		$bIsDeliverySame       = $buer['Customer'][0]["delivery_as_billing"];
		$strDeliveryFirstnames = $buer['Customer'][0]["delivery_name"];
		$strDeliverySurname    = $buer['Customer'][0]["delivery_surname"];
		$strDeliveryAddress1   = $buer['Customer'][0]["delivery_address1"];
		$strDeliveryAddress2   = $buer['Customer'][0]["delivery_address2"];
		$strDeliveryCity       = $buer['Customer'][0]["delivery_address3"];
		$strDeliveryPostCode   = $buer['Customer'][0]["delivery_address4"];
		$strDeliveryCountry    = $buer['Customer'][0]["delivery_address5"];
		$strDeliveryState      = ''; //$buer['Customer'][0]["strDeliveryState"];
		$strDeliveryPhone      = $buer['Customer'][0]["phone2"];
		
		/** Okay, build the crypt field for Form using the information in our session **
		*** First we need to generate a unique VendorTxCode for this transaction **
		*** We're using VendorName, time stamp and a random element.  You can use different methods if you wish **
		*** but the VendorTxCode MUST be unique for each transaction you send to Server **/
		$strTimeStamp = date("ymdHis", time());
		$intRandNum = rand(0,32000)*rand(0,32000);
		$strVendorTxCode = $this->SagePaySettings->strVendorName . "-" . $strTimeStamp . "-" . $intRandNum;
			
		/** Now to calculate the transaction total based on basket contents.  For security **
		*** we recalculate it here rather than relying on totals stored in the session or hidden fields **
		*** We'll also create the basket contents to pass to Form. See the Form Protocol for **
		*** the full valid basket format.  The code below converts from our "x of y" style into **
		*** the system basket format (using a 17.5% VAT calculation for the tax columns) **/
		
		$sngTotal = 0.0;
		$strThisEntry = $this->Session->read('Store.Cart.Items');

		$strBasket="";
		$iBasketItems= 0 ;
		
		
		$sum = 0.00;
		$ilosc_typow = 0;
		$total_amount = 0.00;
		$delivery_costs = 0.00;		
		if( isset($strThisEntry) && is_array($strThisEntry) && !empty($strThisEntry)){
			foreach ($strThisEntry as $item){
				$ilosc_typow =  $item['StoreProduct']['amount'];
				$total_amount += $ilosc_typow;
			
				if($item['StoreProduct']['price_przecena'] > 0 && $item['StoreProduct']['price_promocja'] == 0){
					$price = str_replace(',', '.', $item['StoreProduct']['price_przecena']);	
					$price_detal = str_replace(',', '.', $item['StoreProduct']['price_detaliczna']);								
				}else if( $item['StoreProduct']['price_promocja'] > 0){
					$price = str_replace(',', '.', $item['StoreProduct']['price_promocja']);	
					$price_detal = str_replace(',', '.', $item['StoreProduct']['price_detaliczna']);								
				}else{
					$price = str_replace(',', '.', $item['StoreProduct']['price_detaliczna']);									
				}
				$sum += $ilosc_typow * $price;
				
				// Extract the Quantity and Product from the list of "x of y," entries in the cart
				$iQuantity= $ilosc_typow;
				$iProductId=$item['StoreProduct']['id'];
				// Add another item to our Form basket
				$iBasketItems=$iBasketItems+1;

				
				$strBasket=$strBasket . ":" . $item['StoreProduct']['name'] . ":" . $iQuantity;
				$strBasket=$strBasket . ":" . number_format($price / 1.2,2); /** Price ex-Vat **/
				$strBasket=$strBasket . ":" . number_format($price / 6,2); /** VAT component **/
				$strBasket=$strBasket . ":" . number_format($price,2); /** Item price **/
				$strBasket=$strBasket . ":" . number_format($price * $iQuantity,2); /** Line total **/
				
			} 
		}
		// delivery
		$delivery = $this->Session->read('Store.Cart.Delivery');
		$delivery_costs = 0.0;
		if(isset($delivery) && is_array($delivery) && !empty($delivery)){
			$delivery_costs = $delivery['price'];
			$sum += $delivery['price'];
		} 
		
									
		// We've been right through the cart, so add delivery to the total and the basket
		$sngTotal=$sum;
		$strBasket=$iBasketItems+1 . $strBasket . ":Delivery:1:{$delivery_costs}:---:{$delivery_costs}:{$delivery_costs}";

		// Now to build the Form crypt field.  For more details see the Form Protocol 2.23 
		$strPost = "VendorTxCode=" . $strVendorTxCode; /** As generated above **/
		
		// Optional: If you are a Sage Pay Partner and wish to flag the transactions with your unique partner id, it should be passed here
		if (strlen($this->SagePaySettings->strPartnerID) > 0)
		    $strPost=$strPost . "&ReferrerID=" . $this->SagePaySettings->strPartnerID;
		
		$strPost=$strPost . "&Amount=" . number_format($sngTotal,2); // Formatted to 2 decimal places with leading digit
		$strPost=$strPost . "&Currency=" . $this->SagePaySettings->strCurrency;
		// Up to 100 chars of free format description
		$strPost=$strPost . "&Description=The best DVDs from " . $this->SagePaySettings->strVendorName;
		
		/* The SuccessURL is the page to which Form returns the customer if the transaction is successful 
		** You can change this for each transaction, perhaps passing a session ID or state flag if you wish */
		$strPost=$strPost . "&SuccessURL=" . $this->SagePaySettings->orderSuccessful;
		
		/* The FailureURL is the page to which Form returns the customer if the transaction is unsuccessful
		** You can change this for each transaction, perhaps passing a session ID or state flag if you wish */
		$strPost=$strPost . "&FailureURL=" .  $this->SagePaySettings->orderFailed;
		
		// This is an Optional setting. Here we are just using the Billing names given.
		$strPost=$strPost . "&CustomerName=" . $strBillingFirstnames . " " . $strBillingSurname;
		
		/* Email settings:
		** Flag 'SendEMail' is an Optional setting. 
		** 0 = Do not send either customer or vendor e-mails, 
		** 1 = Send customer and vendor e-mails if address(es) are provided(DEFAULT). 
		** 2 = Send Vendor Email but not Customer Email. If you do not supply this field, 1 is assumed and e-mails are sent if addresses are provided. **/
		if ($this->SagePaySettings->bSendEMail == 0)
		    $strPost=$strPost . "&SendEMail=0";
		else {
		    
		    if ($this->SagePaySettings->bSendEMail == 1) {
		    	$strPost=$strPost . "&SendEMail=1";
		    } else {
		    	$strPost=$strPost . "&SendEMail=2";
		    }
		    
		    if (strlen($strCustomerEMail) > 0)
		        $strPost=$strPost . "&CustomerEMail=" . $strCustomerEMail;  // This is an Optional setting
		    
		    if (($this->SagePaySettings->strVendorEMail <> "[your e-mail address]") && ($this->SagePaySettings->strVendorEMail <> ""))
			    $strPost=$strPost . "&VendorEMail=" . $this->SagePaySettings->strVendorEMail;  // This is an Optional setting
		
		    // You can specify any custom message to send to your customers in their confirmation e-mail here
		    // The field can contain HTML if you wish, and be different for each order.  This field is optional
		    $strPost=$strPost . "&eMailMessage=Thank you so very much for your order.";
		}
		
		// Billing Details:
		$strPost=$strPost . "&BillingFirstnames=" . $strBillingFirstnames;
		$strPost=$strPost . "&BillingSurname=" . $strBillingSurname;
		$strPost=$strPost . "&BillingAddress1=" . $strBillingAddress1;
		if (strlen($strBillingAddress2) > 0) $strPost=$strPost . "&BillingAddress2=" . $strBillingAddress2;
		$strPost=$strPost . "&BillingCity=" . $strBillingCity;
		$strPost=$strPost . "&BillingPostCode=" . $strBillingPostCode;
		$strPost=$strPost . "&BillingCountry=" . $strBillingCountry;
		if (strlen($strBillingState) > 0) $strPost=$strPost . "&BillingState=" . $strBillingState;
		if (strlen($strBillingPhone) > 0) $strPost=$strPost . "&BillingPhone=" . $strBillingPhone;
		
		// Delivery Details:
		$strPost=$strPost . "&DeliveryFirstnames=" . $strDeliveryFirstnames;
		$strPost=$strPost . "&DeliverySurname=" . $strDeliverySurname;
		$strPost=$strPost . "&DeliveryAddress1=" . $strDeliveryAddress1;
		if (strlen($strDeliveryAddress2) > 0) $strPost=$strPost . "&DeliveryAddress2=" . $strDeliveryAddress2;
		$strPost=$strPost . "&DeliveryCity=" . $strDeliveryCity;
		$strPost=$strPost . "&DeliveryPostCode=" . $strDeliveryPostCode;
		$strPost=$strPost . "&DeliveryCountry=" . $strDeliveryCountry;
		if (strlen($strDeliveryState) > 0) $strPost=$strPost . "&DeliveryState=" . $strDeliveryState;
		if (strlen($strDeliveryPhone) > 0) $strPost=$strPost . "&DeliveryPhone=" . $strDeliveryPhone;
		
		
		$strPost=$strPost . "&Basket=" . $strBasket; // As created above 
		
		// For charities registered for Gift Aid, set to 1 to display the Gift Aid check box on the payment pages
		$strPost=$strPost . "&AllowGiftAid=0";
			
		/* Allow fine control over AVS/CV2 checks and rules by changing this value. 0 is Default 
		** It can be changed dynamically, per transaction, if you wish.  See the Server Protocol document */
		if ($this->SagePaySettings->strTransactionType!=="AUTHENTICATE")
			$strPost=$strPost . "&ApplyAVSCV2=0";
			
		/* Allow fine control over 3D-Secure checks and rules by changing this value. 0 is Default 
		** It can be changed dynamically, per transaction, if you wish.  See the Form Protocol document */
		$strPost=$strPost . "&Apply3DSecure=0";
		
		// Encrypt the plaintext string for inclusion in the hidden field
		$strCrypt = $this->encryptAndEncode($strPost);

		$strPurchaseURL = $this->SagePaySettings->strPurchaseURL;
		$strProtocol = $this->SagePaySettings->strProtocol;
		$strTransactionType = $this->SagePaySettings->strTransactionType;
		$strVendorName = $this->SagePaySettings->strVendorName;
		$products = $this->Session->read('Store.Cart.Items');
		
		$this->set(compact('buer','products','strCrypt','strPurchaseURL','strProtocol','strTransactionType','strVendorName'));
	}
	
	
	/* Base 64 Encoding function **
	** PHP does it natively but just for consistency and ease of maintenance, let's declare our own function **/
	function base64Encode($plain) {
	  // Initialise output variable
	  $output = "";
	  
	  // Do encoding
	  $output = base64_encode($plain);
	  
	  // Return the result
	  return $output;
	}

	/* Base 64 decoding function **
	** PHP does it natively but just for consistency and ease of maintenance, let's declare our own function **/
	function base64Decode($scrambled) {
	  // Initialise output variable
	  $output = "";
	  
	  // Fix plus to space conversion issue
	  $scrambled = str_replace(" ","+",$scrambled);
	  
	  // Do encoding
	  $output = base64_decode($scrambled);
	  
	  // Return the result
	  return $output;
	}
	
	
	/*  The SimpleXor encryption algorithm                                                                                **
	**  NOTE: This is a placeholder really.  Future releases of Form will use AES or TwoFish.  Proper encryption      **
	**  This simple function and the Base64 will deter script kiddies and prevent the "View Source" type tampering        **
	**  It won't stop a half decent hacker though, but the most they could do is change the amount field to something     **
	**  else, so provided the vendor checks the reports and compares amounts, there is no harm done.  It's still          **
	**  more secure than the other PSPs who don't both encrypting their forms at all                                      */
	
	function simpleXor($InString, $Key) {
	  // Initialise key array
	  $KeyList = array();
	  // Initialise out variable
	  $output = "";
	  
	  // Convert $Key into array of ASCII values
	  for($i = 0; $i < strlen($Key); $i++){
	    $KeyList[$i] = ord(substr($Key, $i, 1));
	  }
	
	  // Step through string a character at a time
	  for($i = 0; $i < strlen($InString); $i++) {
	    // Get ASCII code from string, get ASCII code from key (loop through with MOD), XOR the two, get the character from the result
	    // % is MOD (modulus), ^ is XOR
	    $output.= chr(ord(substr($InString, $i, 1)) ^ ($KeyList[$i % strlen($Key)]));
	  }
	
	  // Return the result
	  return $output;
	}
	
	
	//** Wrapper function do encrypt an encode based on strEncryptionType setting **
	function encryptAndEncode($strIn) {

		
		if ($this->SagePaySettings->strEncryptionType=="XOR") 
		{
			//** XOR encryption with Base64 encoding **
			return $this->base64Encode($this->simpleXor($strIn,$this->SagePaySettings->strEncryptionPassword));
		} 
		else 
		{
			//** AES encryption, CBC blocking with PKCS5 padding then HEX encoding - DEFAULT **
	
			//** use initialization vector (IV) set from $strEncryptionPassword
	    	$strIV = $this->SagePaySettings->strEncryptionPassword;
				    	
	    	//** add PKCS5 padding to the text to be encypted
	    	$strIn = $this->addPKCS5Padding($strIn);

	    	//** perform encryption with PHP's MCRYPT module
	
			$strCrypt = @mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->SagePaySettings->strEncryptionPassword, $strIn, MCRYPT_MODE_CBC, $strIV);
			
			//** perform hex encoding and return
			return "@" . bin2hex($strCrypt);
		}
	}
	
	
	//** Wrapper function do decode then decrypt based on header of the encrypted field **
	function decodeAndDecrypt($strIn) {

		
		if (substr($strIn,0,1)=="@") 
		{
			//** HEX decoding then AES decryption, CBC blocking with PKCS5 padding - DEFAULT **
			
			//** use initialization vector (IV) set from $strEncryptionPassword
	    	$strIV = $this->SagePaySettings->strEncryptionPassword;

	    	//** remove the first char which is @ to flag this is AES encrypted
	    	$strIn = substr($strIn,1);

	    	//** HEX decoding
	    	$strIn = pack('H*', $strIn);
	    	
	    	//** perform decryption with PHP's MCRYPT module
			return @mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->SagePaySettings->strEncryptionPassword, $strIn, MCRYPT_MODE_CBC, $strIV); 
		} 
		else 
		{
			//** Base 64 decoding plus XOR decryption **
			return $this->simpleXor($this->base64Decode($strIn),$this->SagePaySettings->strEncryptionPassword);
		}
	}
	
	
	//** PHP's mcrypt does not have built in PKCS5 Padding, so we use this
	function addPKCS5Padding($input)
	{
	   $blocksize = 16;
	   $padding = "";
	
	   // Pad input to an even block size boundary
	   $padlength = $blocksize - (strlen($input) % $blocksize);
	   for($i = 1; $i <= $padlength; $i++) {
	      $padding .= chr($padlength);
	   }
	   
	   return $input . $padding;
	}
	
	function getToken($thisString) {
		// List the possible tokens
		$Tokens = array(
			"Status",
			"StatusDetail",
			"VendorTxCode",
			"VPSTxId",
			"TxAuthNo",
			"Amount",
			"AVSCV2", 
			"AddressResult", 
			"PostCodeResult", 
			"CV2Result", 
			"GiftAid", 
			"3DSecureStatus", 
			"CAVV",
			"AddressStatus",
			"CardType",
			"Last4Digits",
			"PayerStatus");

		// Initialise arrays
		$output = array();
		$resultArray = array();
  
		// Get the next token in the sequence
		for ($i = count($Tokens)-1; $i >= 0 ; $i--){
			// Find the position in the string
			$start = strpos($thisString, $Tokens[$i]);
			// If it's present
			if ($start !== false){
				// Record position and token name
				$resultArray[$i]->start = $start;
				$resultArray[$i]->token = $Tokens[$i];
			}
		}
  
		// Sort in order of position
		sort($resultArray);
		// Go through the result array, getting the token values
		for ($i = 0; $i<count($resultArray); $i++){
			// Get the start point of the value
			$valueStart = $resultArray[$i]->start + strlen($resultArray[$i]->token) + 1;
			// Get the length of the value
			if ($i==(count($resultArray)-1)) {
				$output[$resultArray[$i]->token] = substr($thisString, $valueStart);
			} else {
				$valueLength = $resultArray[$i+1]->start - $resultArray[$i]->start - strlen($resultArray[$i]->token) - 2;
				$output[$resultArray[$i]->token] = substr($thisString, $valueStart, $valueLength);
			}      

		}
		// Return the ouput array
		return $output;
	}
}